"""
文件名: conftest.py
描述: pytest配置和共享fixture定义

本模块包含以下主要功能：
1. 测试数据库配置
2. 测试客户端配置
3. 共享的测试数据fixtures
4. 模拟服务配置

依赖模块:
   - pytest: 测试框架
   - httpx: HTTP客户端
   - sqlalchemy: 数据库操作
   - core: 应用核心模块

使用示例:
   >>> @pytest.mark.asyncio
   >>> async def test_example(test_client, test_user):
   >>>     response = await test_client.get("/api/v1/users/me")

注意事项:
   - 使用独立的测试数据库
   - 每个测试后清理数据
   - 模拟外部服务调用
   - 确保测试隔离性

作者: AI助手
创建日期: 2024-06-27
最后修改: 2024-06-27
版本: 1.0.0
"""

import asyncio
import pytest
import pytest_asyncio
from httpx import AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
import uuid
from datetime import datetime

from src.main import app
from src.core.config import get_settings
from src.core.database import get_db, Base
from src.models.user import User, UserInDB
from src.models.category import Category, CategoryInDB
from src.models.channel import Channel, ChannelInDB
from src.models.video import Video, VideoInDB
from src.services.auth_service import AuthService

# 测试配置 - 使用PostgreSQL测试数据库
TEST_DATABASE_URL = "postgresql+asyncpg://_all_topic_rw:ShowAllTopicRW!!!2025@101.35.56.140:5432/show_all_topic_test"

settings = get_settings()


@pytest.fixture(scope="session")
def event_loop():
    """创建事件循环"""
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()


@pytest_asyncio.fixture(scope="session")
async def test_engine():
    """创建测试数据库引擎"""
    engine = create_async_engine(
        TEST_DATABASE_URL,
        echo=False,
    )
    
    # 创建所有表
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
    
    yield engine
    
    # 清理
    await engine.dispose()


@pytest_asyncio.fixture
async def test_session(test_engine):
    """创建测试数据库会话"""
    async_session = async_sessionmaker(
        test_engine, class_=AsyncSession, expire_on_commit=False
    )
    
    async with async_session() as session:
        yield session
        await session.rollback()


@pytest_asyncio.fixture
async def test_client(test_session):
    """创建测试HTTP客户端"""
    # 覆盖数据库依赖
    app.dependency_overrides[get_db] = lambda: test_session
    
    async with AsyncClient(app=app, base_url="http://test") as client:
        yield client
    
    # 清理依赖覆盖
    app.dependency_overrides.clear()


@pytest_asyncio.fixture
async def test_user(test_session) -> UserInDB:
    """创建测试用户"""
    user_data = {
        "id": uuid.uuid4(),
        "email": "test@example.com",
        "username": "testuser",
        "display_name": "Test User",
        "is_active": True,
        "google_id": "test_google_id",
        "created_at": datetime.utcnow(),
        "updated_at": datetime.utcnow(),
    }
    
    user = UserInDB(**user_data)
    test_session.add(user)
    await test_session.commit()
    await test_session.refresh(user)
    
    return user


@pytest_asyncio.fixture
async def test_admin_user(test_session) -> UserInDB:
    """创建测试管理员用户"""
    user_data = {
        "id": uuid.uuid4(),
        "email": "admin@example.com",
        "username": "admin",
        "display_name": "Admin User",
        "is_active": True,
        "is_admin": True,
        "google_id": "admin_google_id",
        "created_at": datetime.utcnow(),
        "updated_at": datetime.utcnow(),
    }
    
    user = UserInDB(**user_data)
    test_session.add(user)
    await test_session.commit()
    await test_session.refresh(user)
    
    return user


@pytest_asyncio.fixture
async def test_category(test_session, test_user) -> CategoryInDB:
    """创建测试分类"""
    category_data = {
        "id": uuid.uuid4(),
        "user_id": test_user.id,
        "name": "测试分类",
        "description": "这是一个测试分类",
        "icon": "test-icon",
        "color": "#FF0000",
        "sort_order": 1,
        "is_default": False,
        "is_active": True,
        "created_at": datetime.utcnow(),
        "updated_at": datetime.utcnow(),
    }
    
    category = CategoryInDB(**category_data)
    test_session.add(category)
    await test_session.commit()
    await test_session.refresh(category)
    
    return category


@pytest_asyncio.fixture
async def test_channel(test_session, test_user) -> ChannelInDB:
    """创建测试频道"""
    channel_data = {
        "id": uuid.uuid4(),
        "youtube_id": "UC_test_channel_id",
        "title": "测试频道",
        "description": "这是一个测试频道",
        "thumbnail_url": "https://example.com/thumbnail.jpg",
        "banner_url": "https://example.com/banner.jpg",
        "subscriber_count": 1000,
        "video_count": 50,
        "view_count": 100000,
        "published_at": datetime.utcnow(),
        "country": "CN",
        "is_active": True,
        "created_at": datetime.utcnow(),
        "updated_at": datetime.utcnow(),
    }
    
    channel = ChannelInDB(**channel_data)
    test_session.add(channel)
    await test_session.commit()
    await test_session.refresh(channel)
    
    return channel


@pytest_asyncio.fixture
async def test_video(test_session, test_channel) -> VideoInDB:
    """创建测试视频"""
    video_data = {
        "id": uuid.uuid4(),
        "youtube_id": "test_video_id",
        "channel_id": test_channel.id,
        "title": "测试视频",
        "description": "这是一个测试视频",
        "thumbnail_url": "https://example.com/video_thumbnail.jpg",
        "duration": "PT10M30S",
        "published_at": datetime.utcnow(),
        "view_count": 1000,
        "like_count": 50,
        "comment_count": 10,
        "tags": ["测试", "视频"],
        "category_id": "22",
        "quality": "hd",
        "is_active": True,
        "created_at": datetime.utcnow(),
        "updated_at": datetime.utcnow(),
    }
    
    video = VideoInDB(**video_data)
    test_session.add(video)
    await test_session.commit()
    await test_session.refresh(video)
    
    return video


@pytest_asyncio.fixture
async def auth_headers(test_user):
    """创建认证头部"""
    # 创建测试JWT token
    auth_service = AuthService()
    access_token = auth_service.create_access_token(
        data={"sub": str(test_user.id)}
    )
    
    return {"Authorization": f"Bearer {access_token}"}


@pytest_asyncio.fixture
async def admin_auth_headers(test_admin_user):
    """创建管理员认证头部"""
    auth_service = AuthService()
    access_token = auth_service.create_access_token(
        data={"sub": str(test_admin_user.id)}
    )
    
    return {"Authorization": f"Bearer {access_token}"}


@pytest.fixture
def mock_youtube_api():
    """模拟YouTube API响应"""
    class MockYouTubeAPI:
        def __init__(self):
            self.channels_response = {
                "items": [
                    {
                        "id": "UC_test_channel_id",
                        "snippet": {
                            "title": "Test Channel",
                            "description": "Test Description",
                            "thumbnails": {
                                "default": {"url": "https://example.com/thumb.jpg"}
                            },
                            "publishedAt": "2022-01-01T00:00:00Z",
                            "country": "US"
                        },
                        "statistics": {
                            "subscriberCount": "1000",
                            "videoCount": "50",
                            "viewCount": "100000"
                        }
                    }
                ]
            }
            
            self.videos_response = {
                "items": [
                    {
                        "id": "test_video_id",
                        "snippet": {
                            "title": "Test Video",
                            "description": "Test Video Description",
                            "thumbnails": {
                                "default": {"url": "https://example.com/video_thumb.jpg"}
                            },
                            "publishedAt": "2022-01-01T00:00:00Z",
                            "channelId": "UC_test_channel_id",
                            "channelTitle": "Test Channel",
                            "tags": ["test"],
                            "categoryId": "22"
                        },
                        "contentDetails": {
                            "duration": "PT10M30S"
                        },
                        "statistics": {
                            "viewCount": "1000",
                            "likeCount": "50",
                            "commentCount": "10"
                        }
                    }
                ]
            }
        
        async def channels(self, **kwargs):
            return self.channels_response
        
        async def videos(self, **kwargs):
            return self.videos_response
        
        async def subscriptions(self, **kwargs):
            return {"items": []}
        
        async def search(self, **kwargs):
            return {"items": []}
    
    return MockYouTubeAPI()


@pytest.fixture
def mock_redis():
    """模拟Redis客户端"""
    class MockRedis:
        def __init__(self):
            self.data = {}
        
        async def get(self, key):
            return self.data.get(key)
        
        async def set(self, key, value):
            self.data[key] = value
        
        async def setex(self, key, ttl, value):
            self.data[key] = value
        
        async def delete(self, key):
            self.data.pop(key, None)
        
        async def exists(self, key):
            return key in self.data
        
        async def keys(self, pattern):
            return list(self.data.keys())
        
        async def incr(self, key):
            current = int(self.data.get(key, 0))
            self.data[key] = str(current + 1)
            return current + 1
        
        async def expire(self, key, ttl):
            pass
        
        def pipeline(self):
            return MockRedisPipeline(self)
    
    class MockRedisPipeline:
        def __init__(self, redis_client):
            self.redis = redis_client
            self.commands = []
        
        def incr(self, key):
            self.commands.append(("incr", key))
            return self
        
        def expire(self, key, ttl):
            self.commands.append(("expire", key, ttl))
            return self
        
        def setex(self, key, ttl, value):
            self.commands.append(("setex", key, ttl, value))
            return self
        
        async def execute(self):
            results = []
            for cmd in self.commands:
                if cmd[0] == "incr":
                    result = await self.redis.incr(cmd[1])
                    results.append(result)
                elif cmd[0] == "expire":
                    await self.redis.expire(cmd[1], cmd[2])
                    results.append(True)
                elif cmd[0] == "setex":
                    await self.redis.setex(cmd[1], cmd[2], cmd[3])
                    results.append(True)
            return results
    
    return MockRedis()


# 测试标记
pytest.mark.unit = pytest.mark.unit
pytest.mark.integration = pytest.mark.integration
pytest.mark.slow = pytest.mark.slow